function GameStateManager() {
	
	this.checkWinStatusCallsCount = 0;
	this.checkWinStatusCallsTimeMs = 0;
	
};
	
GameStateManager.prototype.getNextPossiblBoardStates = function(nextPlayer, currentBoard) {
	var nextBoardStates = new Array();
	if(this.checkWinStatus(currentBoard) == PlayerStatus.NONE) {
		for(var y=0; y<currentBoard.height; y++) {
			for(var x=0; x<currentBoard.width; x++) {
				if(currentBoard.getTokenAt(x, y) == TokenType.NONE) {
					var nextBoardState = currentBoard.copy();
					nextBoardState.move(nextPlayer, x, y);
					nextBoardStates.push(nextBoardState);
				}
			}
		}
	}
	return nextBoardStates;
};

GameStateManager.prototype.checkWinStatus = function(board) {
	var st = new Date().getTime();
	this.checkWinStatusCallsCount++;
	var winStatusToCheck = PlayerStatus.NONE;
	var inARowFound = 0;
	
	//check rows:
	for(var y=0; y<board.height; y++) {
		for(var x=0; x<board.width; x++) {
			var tokenType = board.getTokenAt(x, y);
			if(tokenType == TokenType.X) {
				if(winStatusToCheck != PlayerStatus.PLAYER_ONE_WIN) {
					inARowFound = 0;
				}
				winStatusToCheck = PlayerStatus.PLAYER_ONE_WIN;
				inARowFound++;
				if(inARowFound == board.inARow) {					
					this.checkWinStatusCallsTimeMs += new Date().getTime() - st;
					return PlayerStatus.PLAYER_ONE_WIN;
				}
			} else if(tokenType == TokenType.O) {
				if(winStatusToCheck != PlayerStatus.PLAYER_TWO_WIN) {
					inARowFound = 0;
				}
				winStatusToCheck = PlayerStatus.PLAYER_TWO_WIN;
				inARowFound++;
				if(inARowFound == board.inARow) {
					this.checkWinStatusCallsTimeMs += new Date().getTime() - st;
					return PlayerStatus.PLAYER_TWO_WIN;
				}
			}
		}			
		inARowFound = 0;
	}
	
	//check columns:
	inARowFound = 0;
	for(var x=0; x<=board.width; x++) {
		for(var y=0; y<board.height; y++) {
			var tokenType = board.getTokenAt(x, y);
			if(tokenType == TokenType.X) {
				if(winStatusToCheck != PlayerStatus.PLAYER_ONE_WIN) {
					inARowFound = 0;
				}
				winStatusToCheck = PlayerStatus.PLAYER_ONE_WIN;
				inARowFound++;
				if(inARowFound == board.inARow) {
					this.checkWinStatusCallsTimeMs += new Date().getTime() - st;
					return PlayerStatus.PLAYER_ONE_WIN;
				}
			} else if(tokenType == TokenType.O) {
				if(winStatusToCheck != PlayerStatus.PLAYER_TWO_WIN) {
					inARowFound = 0;
				}
				winStatusToCheck = PlayerStatus.PLAYER_TWO_WIN;
				inARowFound++;
				if(inARowFound == board.inARow) {
					this.checkWinStatusCallsTimeMs += new Date().getTime() - st;
					return PlayerStatus.PLAYER_TWO_WIN;
				}
			}
		}			
		inARowFound = 0;
	}
	
	//check left-side diagonals, left to right: 
	var x = 0;
	var y = board.height-board.inARow;
	var globalY = y;
	inARowFound = 0;
	while(true) {
		var tokenType = board.getTokenAt(x, y);
		if(tokenType == TokenType.X) {
			if(winStatusToCheck != PlayerStatus.PLAYER_ONE_WIN) {
				inARowFound = 0;
			}
			winStatusToCheck = PlayerStatus.PLAYER_ONE_WIN;
			inARowFound++;
			if(inARowFound == board.inARow) {
				this.checkWinStatusCallsTimeMs += new Date().getTime() - st;
				return PlayerStatus.PLAYER_ONE_WIN;
			}
		} else if(tokenType == TokenType.O) {
			if(winStatusToCheck != PlayerStatus.PLAYER_TWO_WIN) {
				inARowFound = 0;
			}
			winStatusToCheck = PlayerStatus.PLAYER_TWO_WIN;
			inARowFound++;
			if(inARowFound == board.inARow) {
				this.checkWinStatusCallsTimeMs += new Date().getTime() - st;
				return PlayerStatus.PLAYER_TWO_WIN;
			}
		}
		x++;
		y++;
		if(x==board.width || y==board.height) {
			globalY--;
			y = globalY;
			x = 0;
			winStatusToCheck = PlayerStatus.NONE;
			inARowFound = 0;
		}
		if(globalY<0) {
			break;
		}
	}
	
	//check top-side diagonals, left to right:
	x = 0;
	var globalX = x;
	y = 0;
	inARowFound = 0;
	while(true) {
		var tokenType = board.getTokenAt(x, y);
		if(tokenType == TokenType.X) {
			if(winStatusToCheck != PlayerStatus.PLAYER_ONE_WIN) {
				inARowFound = 0;
			}
			winStatusToCheck = PlayerStatus.PLAYER_ONE_WIN;
			inARowFound++;
			if(inARowFound == board.inARow) {
				this.checkWinStatusCallsTimeMs += new Date().getTime() - st;
				return PlayerStatus.PLAYER_ONE_WIN;
			}
		} else if(tokenType == TokenType.O) {
			if(winStatusToCheck != PlayerStatus.PLAYER_TWO_WIN) {
				inARowFound = 0;
			}
			winStatusToCheck = PlayerStatus.PLAYER_TWO_WIN;
			inARowFound++;
			if(inARowFound == board.inARow) {
				this.checkWinStatusCallsTimeMs += new Date().getTime() - st;
				return PlayerStatus.PLAYER_TWO_WIN;
			}
		}
		x++;
		y++;
		if(x==board.width || y==board.height) {
			globalX++;
			x = globalX;
			y=0;
			winStatusToCheck = PlayerStatus.NONE;
			inARowFound = 0;
		}
		if(globalX == board.width - board.inARow && y < board.inARow) {
			break;
		}			
	}
	
	//check right-side diagonals, right to left
	var x = board.width-1;
	var y = board.height-board.inARow;
	var globalY = y;
	inARowFound = 0;
	while(true) {
		var tokenType = board.getTokenAt(x, y);
		if(tokenType == TokenType.X) {
			if(winStatusToCheck != PlayerStatus.PLAYER_ONE_WIN) {
				inARowFound = 0;
			}
			winStatusToCheck = PlayerStatus.PLAYER_ONE_WIN;
			inARowFound++;
			if(inARowFound == board.inARow) {
				this.checkWinStatusCallsTimeMs += new Date().getTime() - st;
				return PlayerStatus.PLAYER_ONE_WIN;
			}
		} else if(tokenType == TokenType.O) {
			if(winStatusToCheck != PlayerStatus.PLAYER_TWO_WIN) {
				inARowFound = 0;
			}
			winStatusToCheck = PlayerStatus.PLAYER_TWO_WIN;
			inARowFound++;
			if(inARowFound == board.inARow) {
				this.checkWinStatusCallsTimeMs += new Date().getTime() - st;
				return PlayerStatus.PLAYER_TWO_WIN;
			}
		}
		x--;
		y++;
		if(x==-1 || y==board.height) {
			globalY--;
			y = globalY;
			x = board.width - 1;
			winStatusToCheck = PlayerStatus.NONE;
			inARowFound = 0;
		}
		if(globalY<0) {
			break;
		}
	}
	
	//check top-side diagonals, right to left
	x = board.width - 1;
	var globalX = x;
	y = 0;
	inARowFound = 0;
	while(true) {
		var tokenType = board.getTokenAt(x, y);
		if(tokenType == TokenType.X) {
			if(winStatusToCheck != PlayerStatus.PLAYER_ONE_WIN) {
				inARowFound = 0;
			}
			winStatusToCheck = PlayerStatus.PLAYER_ONE_WIN;
			inARowFound++;
			if(inARowFound == board.inARow) {
				this.checkWinStatusCallsTimeMs += new Date().getTime() - st;
				return PlayerStatus.PLAYER_ONE_WIN;
			}
		} else if(tokenType == TokenType.O) {
			if(winStatusToCheck != PlayerStatus.PLAYER_TWO_WIN) {
				inARowFound = 0;
			}
			winStatusToCheck = PlayerStatus.PLAYER_TWO_WIN;
			inARowFound++;
			if(inARowFound == board.inARow) {
				this.checkWinStatusCallsTimeMs += new Date().getTime() - st;
				return PlayerStatus.PLAYER_TWO_WIN;
			}
		}
		x--;
		y++;
		if(x==-1 || y==board.height) {
			globalX--;
			x = globalX;
			y=0;
			winStatusToCheck = PlayerStatus.NONE;
			inARowFound = 0;
		}
		if(globalX == board.width - board.inARow && y < board.inARow) {
			break;
		}			
	}
	
	//check draw
	if(board.isFull()) {
		this.checkWinStatusCallsTimeMs += new Date().getTime() - st;
		return PlayerStatus.DRAW;
	}
	
	this.checkWinStatusCallsTimeMs += new Date().getTime() - st;
	return PlayerStatus.NONE;
};